#include "ipc_message_parcel.h"

#include <string.h>
#include <unistd.h>

#include <logger.h>

struct MessageHeader {
    int way;
    int pid;
    int cid;
    int sid;
    unsigned long size;
    char buffer[0];
};

IpcMessageParcel::IpcMessageParcel()
{
    callingPid = getpid();
    static int nextCid = 0;
    callingCid = nextCid++;
}

int IpcMessageParcel::Read(int fd)
{
    struct MessageHeader header;
    LOG2SO(DEBUG) << "reading " << fd;
    auto size = read(fd, &header, sizeof(header));
    LOG2SO(DEBUG) << "read from " << fd << ", size: " << size;
    if (size <= 0) {
        return size;
    }

    way = static_cast<Way>(header.way);
    callingPid = header.pid;
    callingCid = header.cid;
    callingSid = header.sid;

    auto message = new char[header.size];
    size = read(fd, message, header.size);
    LOG2SO(DEBUG) << "read from " << fd << ", size: " << size;
    if (size <= 0) {
        return size;
    }

    data = message;
    return size;
}

int IpcMessageParcel::Write(int fd)
{
    auto bufferSize = data.size() + 1;
    auto messageSize = sizeof(MessageHeader) + bufferSize;
    auto message = new char[messageSize];
    struct MessageHeader *header = reinterpret_cast<struct MessageHeader *>(message);
    *header = {
        .way = static_cast<int>(way),
        .pid = callingPid,
        .cid = callingCid,
        .sid = callingSid,
        .size = bufferSize,
    };

    memcpy(header->buffer, data.data(), bufferSize);
    auto size = write(fd, header, messageSize);
    delete[] message;
    return size;
}

bool IpcMessageParcel::operator ==(const IpcMessageParcel &other) const
{
    return way == other.way && callingPid == other.callingPid &&
        callingCid == other.callingCid && callingSid == other.callingSid && data == other.data;
}

std::ostream &operator <<(std::ostream &os, const IpcMessageParcel::Way& way)
{
    switch (way) {
        case IpcMessageParcel::Way::sync:
            os << "Sync";
            break;
        case IpcMessageParcel::Way::syncback:
            os << "SyncBack";
            break;
        case IpcMessageParcel::Way::async:
            os << "Async";
            break;
        case IpcMessageParcel::Way::callback:
            os << "Callback";
            break;
    }
    return os;
}
